#pragma once

namespace matsu
{
	namespace mat
	{
#if _IPHONE_
#pragma mark matrix stack
#endif
		template <typename T>
		void MatrixStack<T>::push(T t) { queue_.push_back(t); }

		template <typename T>
		void MatrixStack<T>::pop() { queue_.pop_back(); }

		template <typename T>
		int MatrixStack<T>::size() const { return queue_.size(); }

		template <typename T>
		T MatrixStack<T>::calc() const
		{
			T result;
			for(int i = 0 ; i < size() ; i++)
			{
				result *= queue_[i];
			}
			return result;
		}

#if _IPHONE_
#pragma mark transform
#endif
		template <typename T>
		Matrix4<T> identity()
		{
			return Matrix4<T>();
		}
		template <typename T>
		Matrix4<T> translate(T x, T y, T z)
		{
			Matrix4<T> m;
			m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
			m.y.x = 0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
			m.z.x = 0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
			m.w.x = x; m.w.y = y; m.w.z = z; m.w.w = 1;
			return m;
		}
		template <typename T>
		Matrix4<T> scale(T s)
		{
			Matrix4<T> m;
			m.x.x = s; m.x.y = 0; m.x.z = 0; m.x.w = 0;
			m.y.x = 0; m.y.y = s; m.y.z = 0; m.y.w = 0;
			m.z.x = 0; m.z.y = 0; m.z.z = s; m.z.w = 0;
			m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
			return m;
		}
		template<typename T>
		Matrix4<T> scale(T x, T y, T z)
		{
			Matrix4<T> m;
			m.x.x = x; m.x.y = 0; m.x.z = 0; m.x.w = 0;
			m.y.x = 0; m.y.y = y; m.y.z = 0; m.y.w = 0;
			m.z.x = 0; m.z.y = 0; m.z.z = z; m.z.w = 0;
			m.w.x = 0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
			return m;
		}
		template <typename T>
		Matrix4<T> xRotate(T degrees)
		{
			T radians = degrees * 3.14159f / 180.0f;
			T s = std::sin(radians);
			T c = std::cos(radians);

			Matrix4<T> m;
			m.x.x = 1; m.x.y = 0; m.x.z = 0; m.x.w = 0;
			m.y.x = 0; m.y.y = c; m.y.z = s; m.y.w = 0;
			m.z.x =  0; m.z.y = -s; m.z.z = c; m.z.w = 0;
			m.w.x =  0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
			return m;
		}
		template <typename T>
		Matrix4<T> yRotate(T degrees)
		{
			T radians = degrees * 3.14159f / 180.0f;
			T s = std::sin(radians);
			T c = std::cos(radians);

			Matrix4<T> m;
			m.x.x =  c; m.x.y = 0; m.x.z = -s; m.x.w = 0;
			m.y.x =  0; m.y.y = 1; m.y.z = 0; m.y.w = 0;
			m.z.x =  s; m.z.y = 0; m.z.z = c; m.z.w = 0;
			m.w.x =  0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
			return m;
		}
		template <typename T>
		Matrix4<T> zRotate(T degrees)
		{
			T radians = degrees * 3.14159f / 180.0f;
			T s = std::sin(radians);
			T c = std::cos(radians);

			Matrix4<T> m;
			m.x.x =  c; m.x.y = s; m.x.z = 0; m.x.w = 0;
			m.y.x = -s; m.y.y = c; m.y.z = 0; m.y.w = 0;
			m.z.x =  0; m.z.y = 0; m.z.z = 1; m.z.w = 0;
			m.w.x =  0; m.w.y = 0; m.w.z = 0; m.w.w = 1;
			return m;
		}

#if _IPHONE_
#pragma mark camera function
#endif
		template<typename T>
		Matrix4<T> ortho(T left, T right, T bottom, T top, T near, T far)
		{
			T a = 2.0f / (right - left);
			T b = 2.0f / (top - bottom);
			T c = 2.0f / (far - near);
			T tx = (right+left) / (right-left);
			T ty = (top+bottom) / (top-bottom);
			T tz = (far+near) / (far-near);

			Matrix4<T> m;
			m.x.x = a;	m.x.y = 0;	m.x.z = 0;	m.x.w = 0;
			m.y.x = 0;	m.y.y = b;	m.y.z = 0;	m.y.w = 0;
			m.z.x = 0;	m.z.y = 0;	m.z.z = c;	m.z.w = 0;
			m.w.x = -tx;	m.w.y = -ty;	m.w.z = -tz;	m.w.w = 1;

			return m;
		}

		template<typename T>
		Matrix4<T> frustum(T left, T right, T bottom, T top, T near, T far)
		{
			T a = 2 * near / (right - left);
			T b = 2 * near / (top - bottom);
			T c = (right + left) / (right - left);
			T d = (top + bottom) / (top - bottom);
			T e = - (far + near) / (far - near);
			T f = -2 * far * near / (far - near);
			Matrix4<T> m;
			m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
			m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
			m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
			m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;

			return m;
		}
		template<typename T>
		Matrix4<T> perspective(T fovy, T aspect, T zNear, T zFar)
		{
			T xmin, xmax, ymin, ymax;

			ymax = static_cast<T>(zNear * tan(fovy * static_cast<T>(3.141592) / 360.0));
			ymin = -ymax;

			xmin = ymin * aspect;
			xmax = ymax * aspect;

			return frustum<T>(xmin, xmax, ymin, ymax, zNear, zFar);
		}

		template<typename T>
		Matrix4<T> lookAt(const matsu::Vector3<T> &eye, const matsu::Vector3<T> &target, const matsu::Vector3<T> &up)
		{
			matsu::Vector3<T> z = (eye-target).normal();
			matsu::Vector3<T> x = up.cross(z).normal();
			matsu::Vector3<T> y = z.cross(x).normal();

			Matrix4<T> m;
			m.x = vec4(x, 0);
			m.y = vec4(y, 0);
			m.z = vec4(z, 0);
			m.w = vec4(0, 0, 0, 1);

			Vector4<T> eyePrime = m * matsu::Vector4<T>(-eye, static_cast<T> (1));
			m = m.transposed();
			m.w = eyePrime;

			return m;
		}
	}
}
